import { useState, useCallback, useEffect } from 'react';
import { ethers, BigNumber } from 'ethers';
import uniswapFactory from '@/abi/uniswapFactory.json';
import uniswapRouter from '@/abi/uniswapRouter.json';
import {
  UNISWAP_FACTORY_ADDRESS,
  UNISWAP_FACTORY_ABI,
  UNISWAP_PAIR_ABI,
  UNISWAP_ROUTER_ADDRESS,
  UNISWAP_ROUTER_ABI,
} from "constants/index";
// UNISWAP_FACTORY_ADDRESS: Uniswap工厂合约的地址。
// UNISWAP_FACTORY_ABI: Uniswap工厂合约的ABI(JSON格式)。
// UNISWAP_PAIR_ABI: Uniswap Pair合约的ABI(JSON格式)。Pair合约是指代表两个代币进行交换的合约。
// UNISWAP_ROUTER_ADDRESS: Uniswap Router合约的地址，用于和Pair合约交互实现代币交换。
// UNISWAP_ROUTER_ABI: Uniswap Router合约的ABI(JSON格式)，包含一些可调用函数的信息，可以更好地理解如何交换代币、设置代币池等操作。


// 
//创建Pair
const useCreatePair = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [createdPairAddress, setCreatedPairAddress] = useState('');

  const createPair = async (tokenA: string, tokenB: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const factory = new ethers.Contract(
        UNISWAP_FACTORY_ADDRESS,
        UNISWAP_FACTORY_ABI,
        signer,
      );
      const tx = await factory.createPair(tokenA, tokenB);
      await tx.wait();
      const filter = factory.filters.PairCreated(null, null, null);
      const logs = await factory.queryFilter(filter, tx.blockHash);
      const pairAddress = logs[0].args.pair;
      setCreatedPairAddress(pairAddress);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [createPair, isLoading, error, createdPairAddress];
};
//获取Pair
const useGetPair = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [pairAddress, setPairAddress] = useState('');

  const getPair = async (tokenA: string, tokenB: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const factory = new ethers.Contract(
        UNISWAP_FACTORY_ADDRESS,
        UNISWAP_FACTORY_ABI,
        signer,
      );
      const pair = await factory.getPair(tokenA, tokenB);
      if (pair === '0x0000000000000000000000000000000000000000') {
        setPairAddress('');
      } else {
        setPairAddress(pair);
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getPair, isLoading, error, pairAddress];
};

//获取所有Pair
const useGetAllPairs = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [allPairs, setAllPairs] = useState([]);

  const getAllPairs = async () => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const factory = new ethers.Contract(
        UNISWAP_FACTORY_ADDRESS,
        UNISWAP_FACTORY_ABI,
        provider,
      );
      const length = await factory.allPairsLength();
      const pairs = [];
      for (let i = 0; i < length; i++) {
        pairs.push(await factory.allPairs(i));
      }
      setAllPairs(pairs);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getAllPairs, isLoading, error, allPairs];
};
//设置收费地址
const useSetFeeTo = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');

  const setFeeTo = async (feeTo: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const factory = new ethers.Contract(
        UNISWAP_FACTORY_ADDRESS,
        UNISWAP_FACTORY_ABI,
        signer,
      );
      await factory.setFeeTo(feeTo);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [setFeeTo, isLoading, error];
};
//获取交易手续费
const useGetFee = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [fee, setFee] = useState(0);

  const getFee = async () => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const factory = new ethers.Contract(UNISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ABI, provider);
      const feeValue = await factory.fee();
      setFee(feeValue);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getFee, isLoading, error, fee];
};
//获取收费地址
const useGetFeeTo = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [feeTo, setFeeTo] = useState("");

  const getFeeTo = async () => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const factory = new ethers.Contract(UNISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ABI, provider);
      const feeToAddress = await factory.feeTo();
      setFeeTo(feeToAddress);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getFeeTo, isLoading, error, feeTo];
};

//获取收费地址的setter
const useGetFeeToSetter = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [feeToSetter, setFeeToSetter] = useState("");

  const getFeeToSetter = async () => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const factory = new ethers.Contract(UNISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ABI, provider);
      const feeToSetterAddress = await factory.feeToSetter();
      setFeeToSetter(feeToSetterAddress);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getFeeToSetter, isLoading, error, feeToSetter];
};



//获取交易对信息

const useGetPairInfo = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [pairInfo, setPairInfo] = useState(null);

  const getPairInfo = async (pairAddress: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const pair = new ethers.Contract(pairAddress, UNISWAP_PAIR_ABI, provider);
      const [token0, token1] = await Promise.all([pair.token0(), pair.token1()]);
      const [reserve0, reserve1] = await Promise.all([pair.getReserves()]);
      const totalSupply = await pair.totalSupply();
      setPairInfo({ token0, token1, reserve0, reserve1, totalSupply });
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [getPairInfo, isLoading, error, pairInfo];
};


//交换代币

const useSwapTokens = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [txHash, setTxHash] = useState("");

  const swapTokens = async (tokenIn: string, tokenOut: string, amountIn: number, amountOutMin: number, recipient: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const router = new ethers.Contract(UNISWAP_ROUTER_ADDRESS, UNISWAP_ROUTER_ABI, signer);
      const tokenInContract = new ethers.Contract(tokenIn, UNISWAP_PAIR_ABI, signer);
      const tokenInSymbol = await tokenInContract.symbol();
      const tokenOutContract = new ethers.Contract(tokenOut, UNISWAP_PAIR_ABI, signer);
      const tokenOutSymbol = await tokenOutContract.symbol();
      const amountInWei = ethers.utils.parseUnits(amountIn.toString(), "ether");
      const amountOutMinWei = ethers.utils.parseUnits(amountOutMin.toString(), "ether");
      const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
      const tx = await router.swapExactTokensForTokens(amountInWei, amountOutMinWei, [tokenIn, tokenOut], recipient, deadline);
      await tx.wait();
      setTxHash(tx.hash);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [swapTokens, isLoading, error, txHash];
};

//添加流动性
const useAddLiquidity = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [txHash, setTxHash] = useState("");

  const addLiquidity = async (
    tokenA: string,
    tokenB: string,
    amountADesired: number,
    amountBDesired: number,
    amountAMin: number,
    amountBMin: number,
    recipient: string,
    deadline: number
  ) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const router = new ethers.Contract(UNISWAP_ROUTER_ADDRESS, UNISWAP_ROUTER_ABI, signer);
      const tokenAContract = new ethers.Contract(tokenA, UNISWAP_PAIR_ABI, signer);
      const tokenASymbol = await tokenAContract.symbol();
      const tokenBContract = new ethers.Contract(tokenB, UNISWAP_PAIR_ABI, signer);
      const tokenBSymbol = await tokenBContract.symbol();
      const amountAWei = ethers.utils.parseUnits(amountADesired.toString(), "ether");
      const amountBWei = ethers.utils.parseUnits(amountBDesired.toString(), "ether");
      const amountAMinWei = ethers.utils.parseUnits(amountAMin.toString(), "ether");
      const amountBMinWei = ethers.utils.parseUnits(amountBMin.toString(), "ether");
      const tx = await router.addLiquidity(
        tokenA,
        tokenB,
        amountAWei,
        amountBWei,
        amountAMinWei,
        amountBMinWei,
        recipient,
        deadline
      );
      await tx.wait();
      setTxHash(tx.hash);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return [addLiquidity, isLoading, error, txHash];
};


//移除流动性

const useRemoveLiquidity = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [txHash, setTxHash] = useState("");

  const removeLiquidity = async (pairAddress: string, liquidity: number, 
    amountAMin: number, amountBMin: number, recipient: string) => {
    try {
      setIsLoading(true);
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const pair = new ethers.Contract(pairAddress, UNISWAP_PAIR_ABI, signer);
      const token0 = await pair.token0();
      const token1 = await pair.token1();
      const decimals0 = await getTokenDecimals(token0, provider);
      const decimals1 = await getTokenDecimals(token1, provider);
      const amountAMinWei = ethers.utils.parseUnits(amountAMin.toString(), decimals0);
      const amountBMinWei = ethers.utils.parseUnits(amountBMin.toString(), decimals1);
      const tx = await pair.approve(UNISWAP_PAIR_ABI.UNISWAP_ROUTER_ADDRESS, liquidity);
      await tx.wait();
      const router = new ethers.Contract(UNISWAP_ROUTER_ADDRESS, UNISWAP_ROUTER_ABI, signer);
      const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
      // 20 minutes from the current Unix time const tx2 = await router.removeLiquidity(token0, token1, liquidity, amountAMinWei, amountBMinWei, recipient, deadline); await tx2.wait(); setTxHash(tx2.hash); } catch (err) { setError(err.message); } finally { setIsLoading(false); } };

      const getTokenDecimals = async (tokenAddress: string, provider: ethers.providers.Web3Provider) => {
        const token = new ethers.Contract(tokenAddress, UNISWAP_PAIR_ABI, provider);
        const decimals = await token.decimals(); return decimals;
      }
      return [removeLiquidity, isLoading, error, txHash];
    } catch()

    }}


  const useGetTransactionHistory = () => {
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const [transactions, setTransactions] = useState([]);

    const getTransactionHistory = async (tokenA: string, tokenB: string) => {
      try {
        setIsLoading(true);
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const factory = new ethers.Contract(UNISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ABI, provider);
        const pairAddress = await factory.getPair(tokenA, tokenB);
        const pair = new ethers.Contract(pairAddress, UNISWAP_PAIR_ABI, provider);

        // get past events from pair contract
        const pastEvents = await pair.queryFilter(pair.filters.Swap(null, null, null), -100); // last 100 events
        const transactions = await Promise.all(
          pastEvents.map(async (event) => {
            const tx = await provider.getTransaction(event.transactionHash);
            const tokenIn = tokenA === event.args.token0 ? event.args.amount0In : event.args.amount1In;
            const tokenOut = tokenA === event.args.token0 ? event.args.amount1Out : event.args.amount0Out;
            const fees = ethers.utils.formatEther(tx.gasPrice.mul(21000));
            return { tokenIn, tokenOut, fees, txHash: event.transactionHash };
          })
        );

        setTransactions(transactions);
      } catch (err) {
        setError(err.message);
      } finally {
        setIsLoading(false);
      }
    };

    return [getTransactionHistory, isLoading, error, transactions];
  };


  const useGetAccountTransactionHistory = () => {
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const [transactions, setTransactions] = useState([]);

    const getAccountTransactionHistory = async (account: string, tokenA: string, tokenB: string) => {
      try {
        setIsLoading(true);
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const factory = new ethers.Contract(UNISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ABI, provider);
        const pairAddress = await factory.getPair(tokenA, tokenB);
        const pair = new ethers.Contract(pairAddress, UNISWAP_PAIR_ABI, provider);

        // query all Swap events for the given account
        const filter = {
          address: pair.address,
          fromBlock: 0,
          toBlock: "latest",
          topics: [
            ethers.utils.id("Swap(address,uint256,uint256,uint256,address)"),
            null,
            ethers.utils.hexZeroPad(account, 32),
            null,
            null,
          ],
        };
        const events = await provider.getLogs(filter);

        const transactions = await Promise.all(
          events.map(async (event) => {
            const tx = await provider.getTransaction(event.transactionHash);
            const tokenA = await pair.token0(); const tokenB = await pair.token1(); const tokenIn = account === ethers.utils.getAddress(event.topics[2]) ? event.data.slice(0, 66) : event.data.slice(66); const tokenOut = account === ethers.utils.getAddress(event.topics[2]) ? event.data.slice(66) : event.data.slice(0, 66); const amountIn = ethers.utils.formatUnits(tokenIn, await getTokenDecimals(tokenA, provider)); const amountOut = ethers.utils.formatUnits(tokenOut, await getTokenDecimals(tokenB, provider)); const fees = ethers.utils.formatEther(tx.gasPrice.mul(event.gasUsed)); return { tokenA, tokenB, amountIn, amountOut, fees, txHash: event.transactionHash };
          }));

        setTransactions(transactions);
      } catch (err) {
        setError(err.message);
      } finally {
        setIsLoading(false);
      }
    };

    const getTokenDecimals = async (tokenAddress: string, provider: ethers.providers.Web3Provider) => { const token = new ethers.Contract(tokenAddress, UNISWAP_PAIR_ABI, provider); const decimals = await token.decimals(); return decimals; };

    return [getAccountTransactionHistory, isLoading, error, transactions];
  }